//
//  ShaderNode.cpp
//  client
//
//  Created by will on 03/11/2017.
//
//

#include "ShaderNode.hpp"

USING_NS_CC;

ShaderNode::ShaderNode()
:_center(Vec2(0.0f, 0.0f))
,_resolution(Vec2(0.0f, 0.0f))
{
}

ShaderNode::~ShaderNode()
{
}

ShaderNode* ShaderNode::shaderNodeWithVertex(const std::string &vert, const std::string& frag)
{
    auto node = new (std::nothrow) ShaderNode();
    node->initWithVertex(vert, frag);
    node->autorelease();
    
    return node;
}

bool ShaderNode::initWithVertex(const std::string &vert, const std::string &frag)
{
    _vertFileName = vert;
    _fragFileName = frag;
    
#if CC_ENABLE_CACHE_TEXTURE_DATA
    auto listener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom* event){
        this->setGLProgramState(nullptr);
        loadShaderVertex(_vertFileName, _fragFileName);
    });
    
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
#endif
    
    loadShaderVertex(vert, frag);
    
    auto& vp = Camera::getDefaultCamera()->getDefaultViewport();
    auto size = Director::getInstance()->getWinSize();
    _resolution = Vec2(vp._width, vp._height);
    
    setContentSize(size);
    setAnchorPoint(Vec2(0.5f, 0.5f));
    
    return true;
}

void ShaderNode::loadShaderVertex(const std::string &vert, const std::string &frag)
{
    
    std::string key = vert+frag;
    
    auto glprogram = GLProgramCache::getInstance()->getGLProgram(key);
    if(!glprogram){
        auto fileUtiles = FileUtils::getInstance();
        
        // frag
        auto fragmentFilePath = fileUtiles->fullPathForFilename(frag);
        auto fragSource = fileUtiles->getStringFromFile(fragmentFilePath);
        
        // vert
        std::string vertSource;
        if (vert.empty()) {
            vertSource = ccPositionTextureColor_vert;
        } else {
            std::string vertexFilePath = fileUtiles->fullPathForFilename(vert);
            vertSource = fileUtiles->getStringFromFile(vertexFilePath);
        }
        
        glprogram = GLProgram::createWithByteArrays(vertSource.c_str(), fragSource.c_str());
        GLProgramCache::getInstance()->addGLProgram(glprogram, key);
    }

    auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
    setGLProgramState(glprogramstate);
}

void ShaderNode::setBlendFunc(const BlendFunc &blendFunc)
{
    _blendFunc=blendFunc;
}

const BlendFunc &ShaderNode::getBlendFunc() const
{
    return _blendFunc;
}

void ShaderNode::setPosition(const Vec2 &newPosition)
{
    Node::setPosition(newPosition);
    auto position = getPosition();
    auto frameSize = Director::getInstance()->getOpenGLView()->getFrameSize();
    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto retinaFactor = Director::getInstance()->getOpenGLView()->getRetinaFactor();
    _center = Vec2(position.x * frameSize.width / visibleSize.width * retinaFactor, position.y * frameSize.height / visibleSize.height * retinaFactor);
}

void ShaderNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    _customCommand.init(_globalZOrder, transform, flags);
    _customCommand.func = CC_CALLBACK_0(ShaderNode::onDraw, this, transform, flags);
    renderer->addCommand(&_customCommand);
}

void ShaderNode::onDraw(const Mat4 &transform, uint32_t flags)
{
    float w = getContentSize().width, h = getContentSize().height;
    const Color3B& color3 = getColor();
    float r = color3.r/255.0f;
    float g = color3.g/255.0f;
    float b = color3.b/255.0f;
    float a = getOpacity()/255.0f;

    const Rect rect = {0,0,w,h};
    
    
    GLfloat    coordinates[] = {
        0.0f,    1.0f,
        1.0f,   1.0f,
        0.0f,    0.0f,
        1.0f,   0.f };
    
    GLfloat  vertices[] = {    rect.origin.x,        rect.origin.y,                            /*0.0f,*/
        rect.origin.x + rect.size.width,        rect.origin.y,                            /*0.0f,*/
        rect.origin.x,                            rect.origin.y + rect.size.height,        /*0.0f,*/
        rect.origin.x + rect.size.width,        rect.origin.y + rect.size.height,        /*0.0f*/ };
    
    
    GLfloat colors[] = {
        r,g,b,a,
        r,g,b,a,
        r,g,b,a,
        r,g,b,a,
    };
    
    GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_TEX_COORD );
    
    auto glProgramState = getGLProgramState();
    glProgramState->setUniformVec2("u_resolution", _resolution);
    glProgramState->setUniformVec2("u_center", _center);
    glProgramState->setVertexAttribPointer("a_position", 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glProgramState->setVertexAttribPointer("a_texCoord", 2, GL_FLOAT, GL_FALSE, 0, coordinates);
    glProgramState->setVertexAttribPointer("a_color", 4, GL_FLOAT, GL_FALSE, 0, colors);
    
    
    glProgramState->apply(transform);
    
    const auto& blendFunc = getBlendFunc();
    GL::blendFunc(blendFunc.src, blendFunc.dst);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4);
}
